/* 
 * Persistence4J - Simple library for data persistence using java
 * Copyright (c) 2010, Avdhesh yadav.
 * http://www.avdheshyadav.com
 * Contact: avdhesh.yadav@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */
package com.avdheshyadav.p4j.common;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 
 * @author Avdhesh Yadav
 *
 */
public class ReflectionUtil
{
	
	/**
	 * 
	 * @param className String
	 * 
	 * @return Object
	 * 
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 */
	public static Object newInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException
	{
		Object object = null;
		try 
		{
			object = (Object)Class.forName(className).newInstance();
		}
		catch (ClassNotFoundException e)
		{
			ClassLoader cl = Thread.currentThread().getContextClassLoader();
			object = (Object)cl.loadClass(className).newInstance();
		}
		return object;
	}


	/**
	 * 
	 * @param object Object
	 * @param fieldName String
	 * @param newValue Object
	 * 
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public static void setFieldValue(Object object, String fieldName, Object newValue) throws NoSuchFieldException,IllegalArgumentException, IllegalAccessException
	{
		Class<?> clazz = object.getClass();
		Field field = getDeclaredField(object, fieldName, true);
		field.setAccessible(true);
		field.set(object, newValue);
	}


	/**
	 * 
	 * @param object Object
	 * @param methodName String 
	 * @param arguments Object[]
	 * 
	 * @return Object
	 * 
	 * @throws NoSuchMethodException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	public static Object invokeMethod(Object object, String methodName, Object[] arguments) throws NoSuchMethodException,IllegalArgumentException, IllegalAccessException,InvocationTargetException 
	{
		Method method = getDeclaredMethod(object, methodName, true);
		method.setAccessible(true);
		return method.invoke(object, arguments);
	}
	
	
	/**
	 * 
	 * @param object Object
	 * @param name String
	 * 
	 * @return Method
	 * 
	 * @throws NoSuchMethodException
	 */
	public static Method getDeclaredMethod(Object object, String name, boolean searchSuperMethods)throws NoSuchMethodException 
	{
		Class<?> clazz = object.getClass();
		Method method = clazz.getDeclaredMethod(name);
		
		if(method == null && searchSuperMethods)
		{
			while(method == null & (clazz = clazz.getSuperclass()) != null)
				method = clazz.getDeclaredMethod(name);
		}
		
		if (method == null) 
			throw new NoSuchMethodException();
		
		return method;
	}

	
	/**
	 * 
	 * @param object Object
	 * @param name String
	 * @param includeSuperFields
	 * 
	 * @return boolean
	 * 
	 * @throws NoSuchFieldException
	 */
	public static Field getDeclaredField(Object object, String name, boolean includeSuperFields)throws NoSuchFieldException 
	{
		Class<?> clazz = object.getClass();
		Field field = clazz.getDeclaredField(name);
		
		if(field == null && includeSuperFields)
		{
			while(field == null & (clazz = clazz.getSuperclass()) != null)
				field = clazz.getDeclaredField(name);
		}
		
		if (field == null)
			throw new NoSuchFieldException();

		return field;
	}

	
	/**
	 * 
	 * @param clazz Class
	 * 
	 * @param includeSuperFields  boolean
	 * 
	 * @return List<Field>
	 */
	public static List<Field> getDeclaredFields(Class clazz, boolean includeSuperFields) 
	{
		List<Field> fields = new ArrayList<Field>();
		
		fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
		
		// super Class field True
		if(includeSuperFields)		
		{	
			while ((clazz = clazz.getSuperclass()) != null)		
				fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
		}
		return fields;
	}
	
	
	/**
	 * 
	 * @param clazz Class
	 * 
	 * @param includeSuperFields  boolean
	 * 
	 * @return List<Method>
	 */
	public static List<Method> getDeclaredMethods(Class clazz, boolean includeSuperMethods) 
	{
		List<Method> methods = new ArrayList<Method>();
		methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
		// super Class field True
		if(includeSuperMethods)		
		{	
			while ((clazz = clazz.getSuperclass()) != null)		
				methods.addAll(Arrays.asList(clazz.getDeclaredMethods()));
		}
		return methods;
	}
}
